# coding=utf-8
import binascii
import struct
from time import sleep

from bluepy import btle
from bluepy.btle import DefaultDelegate, UUID, Peripheral, Scanner, BTLEDisconnectError, BTLEGattError

# 这个是手环的service uuid和Characteristic uuid，出厂已经固定

service_uuid = UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
char_uuid = UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")


class SocketIoDelegate(DefaultDelegate):
    def __init__(self, socketio):
        DefaultDelegate.__init__(self)
        self.socket_io = socketio

    # func is caled on notifications
    def handleNotification(self, cHandle, data):
        b = bytearray()
        b.extend(data)
        print binascii.hexlify(b)
        self.socket_io.emit('server_response', {'data': binascii.hexlify(b)}, namespace='/test')


class HandlerDelegate(DefaultDelegate):
    # Constructor (run once on startup)
    def __init__(self):
        DefaultDelegate.__init__(self)

    # func is caled on notifications
    def handleNotification(self, cHandle, data):
        b = bytearray()
        b.extend(data)
        print binascii.hexlify(b)


def init_bracelet(p):
    service = p.getServiceByUUID(service_uuid)
    character = service.getCharacteristics(char_uuid)[0]
    hnd = character.getHandle()
    # Search and get Get The Bracelet-Characteristics "property" (UUID-0x2902 CCC-Client Characteristic Configuration))
    #  wich is located in a handle in the range defined by the boundries of the BraceletService
    # for descriptor in p.getDescriptors(hnd, 0x00F):  # The handle range should be read from the services
    #     if descriptor.uuid == 0x2902:  # but is not done due to a Bluez/BluePy bug :(
    #         print ("Bracelet Client Characteristic Configuration found at handle 0x"
    #                + format(descriptor.handle, "02X"))
    #         h_bracelet_ccc = descriptor.handle

    # Turn notifications on by setting bit0 in the CCC more info on:
    # https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
    p.writeCharacteristic(hnd + 1, struct.pack('<bb', 0x01, 0x00))
    print "Notification is turned on for Bracelet"


def main_loop(p):
    # Main loop --------
    while True:
        if p.waitForNotifications(3.0):
            continue

        print "Waiting... Waited more than one sec for notification"


def reconnect(addr, type):
    while True:
        try:
            p = Peripheral(addr, type)
            p.setDelegate(HandlerDelegate())
            print u'bracelet {} connected '.format(addr)
            main_loop(p)
        except BTLEDisconnectError:
            print u'disconnect from bracelet {}'.format(addr)
        except BTLEGattError:
            print u'connect to bracelet {} fail'.format(addr)
        except:
            print u'无法连接 {}'.format(addr)
        else:
            print u'connection from bracelet {}is down'.format(addr)
        sleep(2.0)


def reconnectNew(addr, type, socketio):
    while True:
        try:
            p = Peripheral(addr, type)
            p.setDelegate(SocketIoDelegate(socketio))
            init_bracelet(p)
            print u'bracelet {} connected '.format(addr)
            while p:
                if p.waitForNotifications(1):
                    continue
                else:
                    socketio.sleep(1)

        except BTLEDisconnectError:
            print u'disconnect from bracelet {}'.format(addr)
        except BTLEGattError:
            print u'connect to bracelet {} fail'.format(addr)
        else:
            print u'connection from bracelet {}is down'.format(addr)
        socketio.sleep(1)


def scan():
    d = {}
    scanner = Scanner()
    bracelet = None
    while bracelet is None:
        devices = scanner.scan(10.0)
        for dev in devices:
            # print "Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi)
            for (adtype, desc, value) in dev.getScanData():
                if desc == 'Manufacturer':
                    d['Manufacturer'] = value
                if desc == "Complete Local Name":
                    d['CompleteLocalName'] = value

            if 'Manufacturer' in d.keys() and d['Manufacturer'].startswith('05ff55aa0fac'):
                print 'find you! %s %s ' % (dev.addr, dev.addrType)
                bracelet = dev
                break
        print 'bracelet not in range, continue'
    return bracelet


mac = "c1:20:64:77:4f:7c"

if __name__ == "__main__":
    btle.Debugging = True
    bracelet = scan()

    # p = Peripheral(bracelet.addr, bracelet.addrType)
    reconnect(bracelet.addr, bracelet.addrType)
